<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="深入理解java虚拟机,">










<meta name="description" content="深入理解java虚拟机 jvm的内存区域， Hotspot在jdk7中将方法区和字符串常量池移动到堆中了 jdk8移除了方法去 使用元空间替代，元空间也使用堆内存 1， 程序计数器，每个线程私有记录当前线程所执行指令的行号， 根据这个行号方便程序进行调整， 比如循环，分支，跳转， 异常跳转等 2. 虚拟机栈   每个线程独有每个方法在执行的时候都会产生一个栈帧， 方法里面的局部变量（值或者应用）">
<meta name="keywords" content="深入理解java虚拟机">
<meta property="og:type" content="article">
<meta property="og:title" content="深入理解java虚拟机">
<meta property="og:url" content="https://blog.xiezc.xyz/2019/04/03/深入理解java虚拟机/index.html">
<meta property="og:site_name" content="xiezc的小站">
<meta property="og:description" content="深入理解java虚拟机 jvm的内存区域， Hotspot在jdk7中将方法区和字符串常量池移动到堆中了 jdk8移除了方法去 使用元空间替代，元空间也使用堆内存 1， 程序计数器，每个线程私有记录当前线程所执行指令的行号， 根据这个行号方便程序进行调整， 比如循环，分支，跳转， 异常跳转等 2. 虚拟机栈   每个线程独有每个方法在执行的时候都会产生一个栈帧， 方法里面的局部变量（值或者应用）">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2019-04-03T16:30:53.576Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="深入理解java虚拟机">
<meta name="twitter:description" content="深入理解java虚拟机 jvm的内存区域， Hotspot在jdk7中将方法区和字符串常量池移动到堆中了 jdk8移除了方法去 使用元空间替代，元空间也使用堆内存 1， 程序计数器，每个线程私有记录当前线程所执行指令的行号， 根据这个行号方便程序进行调整， 比如循环，分支，跳转， 异常跳转等 2. 虚拟机栈   每个线程独有每个方法在执行的时候都会产生一个栈帧， 方法里面的局部变量（值或者应用）">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://blog.xiezc.xyz/2019/04/03/深入理解java虚拟机/">





  <title>深入理解java虚拟机 | xiezc的小站</title>
  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?8ae02d8cf311eabbae2621ff2d51a260";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">xiezc的小站</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">xiezc的小站</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://blog.xiezc.xyz/2019/04/03/深入理解java虚拟机/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="xiezc">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="xiezc的小站">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">深入理解java虚拟机</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-04-03T23:29:59+08:00">
                2019-04-03
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/java/" itemprop="url" rel="index">
                    <span itemprop="name">java</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          
             <span id="/2019/04/03/深入理解java虚拟机/" class="leancloud_visitors" data-flag-title="深入理解java虚拟机">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">阅读次数&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p><strong>深入理解java虚拟机</strong></p>
<p>jvm的内存区域， Hotspot在jdk7中将方法区和字符串常量池移动到堆中了</p>
<p>jdk8移除了方法去 使用元空间替代，元空间也使用堆内存</p>
<h2 id="1，-程序计数器，每个线程私有"><a href="#1，-程序计数器，每个线程私有" class="headerlink" title="1， 程序计数器，每个线程私有"></a>1， 程序计数器，每个线程私有</h2><p>记录当前线程所执行指令的行号， 根据这个行号方便程序进行调整， 比如循环，分支，跳转， 异常跳转等</p>
<h2 id="2-虚拟机栈-每个线程独有"><a href="#2-虚拟机栈-每个线程独有" class="headerlink" title="2. 虚拟机栈   每个线程独有"></a>2. 虚拟机栈   每个线程独有</h2><p>每个方法在执行的时候都会产生一个栈帧， 方法里面的局部变量（值或者应用） 就放入栈帧中。 执行完了这个栈帧就退出栈了， 所以不需要额外垃圾回收机制， 但是如果太深的方法调用（递归）就可能耗尽栈内存，导致栈溢出异常</p>
<p>\3. 本地方法栈，  与虚拟机栈类似， jvm规范中并没有强制要求实现这个栈， 有的虚拟机实现就没有这个区间， 而是和虚拟机栈合并</p>
<p>本地方法栈是jvm执行一些本地方法使用到的栈，  而虚拟机栈是执行java方法使用到的</p>
<h2 id="4，java的堆，-所有线程共享的内存区域，"><a href="#4，java的堆，-所有线程共享的内存区域，" class="headerlink" title="4，java的堆，  所有线程共享的内存区域，"></a>4，java的堆，  所有线程共享的内存区域，</h2><p>虚拟机中最大的一块内存区域， 垃圾回收器主要工作的区域， 几乎所有的对象实例都是存放于这块区域， 对象的成员变量就算是基本类型也是存放在这块区域的。 但是随着JIT编译器的发展， 一些优化技术的实现， 导致有对象有可能不在堆上。</p>
<h3 id="堆还可以细分成老年代和新生代区域"><a href="#堆还可以细分成老年代和新生代区域" class="headerlink" title="堆还可以细分成老年代和新生代区域"></a>堆还可以细分成老年代和新生代区域</h3><p>划分这两个区域的目的是为了更好的垃圾回收和内存分配。 堆可以在内存中处于物理上的不连续区间，但是逻辑上连续。 大多数的jvm实现都是堆内存可以动态扩展的， 当堆无法再扩展的时候就会抛出outofmemoryerror异常</p>
<h2 id="5-方法区，-各个线程共享的区域"><a href="#5-方法区，-各个线程共享的区域" class="headerlink" title="5. 方法区， 各个线程共享的区域"></a>5. 方法区， 各个线程共享的区域</h2><p>存储被虚拟机加载的类的信息， 常量？？？， 静态变量， 即时编译器编译后的代码等。 </p>
<p>应用hotspot虚拟机的垃圾回收作用到方法区了， 并且GC把方法区看成是与老年代更老的永久代来处理的。 方法区实行jvm规范没有定义， 不同虚拟机有不同表现（String.intern（））。 并且永久代有内存上限限制， 很容易出现内存溢出问题， </p>
<p>jdk7中已经把永久代中的字符串常量池移除方法区，放入物理内存。 jdk8已经把方法区移除到物理内存区域，  永久区的垃圾回收主要是对常量池的回收和类的卸载（是这个类信息在用到一次后再也不使用了）， 这区域的回收效果不好， 类的卸载条件十分苛刻。 元空间与类加载器绑定，元空间内存由元空间虚拟机管理</p>
<h4 id="元空间的特性"><a href="#元空间的特性" class="headerlink" title="元空间的特性"></a>元空间的特性</h4><p>充分利用了Java语言规范中的好处：类及相关的元数据的生命周期与类加载器的一致</p>
<p>每个加载器有专门的存储空间</p>
<p>只进行线性分配</p>
<p>不会单独回收某个类</p>
<p>省掉了GC扫描及压缩的时间</p>
<p>元空间里的对象的位置是固定的</p>
<h4 id="如果GC发现某个类加载器不再存活了，会把相关的空间整个回收掉元空间的内存分配模型"><a href="#如果GC发现某个类加载器不再存活了，会把相关的空间整个回收掉元空间的内存分配模型" class="headerlink" title="如果GC发现某个类加载器不再存活了，会把相关的空间整个回收掉元空间的内存分配模型"></a>如果GC发现某个类加载器不再存活了，会把相关的空间整个回收掉元空间的内存分配模型</h4><p>绝大多数的类元数据的空间都从本地内存中分配</p>
<p>用来描述类元数据的类也被删除了</p>
<p>分元数据分配了多个虚拟内存空间</p>
<p>给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; sun/反射/代理对应的类加载器的块会小一些</p>
<p>归还内存块，释放内存块列表</p>
<p>一旦元空间的数据被清空了，虚拟内存的空间会被回收掉</p>
<p>减少碎片的策略 </p>
<p>我们来看下JVM是如何给元数据分配虚拟内存的空间的 </p>
<p>-——————– </p>
<p>作者：zxcodestudy </p>
<p>来源：CSDN </p>
<p>原文：<a href="https://blog.csdn.net/qq_16681169/article/details/70471010" target="_blank" rel="noopener">https://blog.csdn.net/qq_16681169/article/details/70471010</a> </p>
<p>版权声明：本文为博主原创文章，转载请附上博文链接！</p>
<h3 id="运行时常量池，-也是在方法区中"><a href="#运行时常量池，-也是在方法区中" class="headerlink" title="运行时常量池， 也是在方法区中"></a>运行时常量池， 也是在方法区中</h3><p>在方法的栈帧中（方法运行时）也生成一些常量， 这些常量就放在运行时常量池， 这部分的内存会被垃圾回收 </p>
<h3 id="直接内存"><a href="#直接内存" class="headerlink" title="直接内存"></a>直接内存</h3><p>java由一些方法可以直接分配堆外的物理内存，DirectByteBuffer对象作为这块内存的引用。</p>
<p>netty框架就使用这块内存来大量提高性能</p>
<h1 id="垃圾回收机制和内存分配"><a href="#垃圾回收机制和内存分配" class="headerlink" title="垃圾回收机制和内存分配"></a>垃圾回收机制和内存分配</h1><h3 id="引用计数算法"><a href="#引用计数算法" class="headerlink" title="引用计数算法"></a>引用计数算法</h3><p>很简单的一个判断对象是否存活的算法， 当对象多一个引用的时候， 对象的引用计数就加一， 少一个引用的时候就计数减去一， 如果计数为0 就是垃圾对象， 可以回收</p>
<p>这是一个很简单高效的内存回收算法， 但是有一个致命缺陷， 很难解决对象之间的循环引用的问题，所以主流的jvm实现的gc都没有使用这个算法</p>
<h4 id="可达性分析算法，-主流的jvm都是使用这个算法"><a href="#可达性分析算法，-主流的jvm都是使用这个算法" class="headerlink" title="可达性分析算法， 主流的jvm都是使用这个算法"></a>可达性分析算法， 主流的jvm都是使用这个算法</h4><p>就是根据一些GC Roots对象的引用关系来遍历这个堆对象， 没有被遍历到的对象就是垃圾对象就可以被回收。</p>
<p>其中GC Roots对象有</p>
<p>l  虚拟机栈的栈帧的局部变量表所引用的对象；</p>
<p>​           栈帧中的变量就是正在运行中的局部变量， 马上就要使用到的， 当然不能回收</p>
<p>l  本地方法栈的JNI所引用的对象；</p>
<p>同理</p>
<p>l  方法区的<strong>静态变量</strong>和<strong>常量</strong>所引用的对象；</p>
<p>静态变量说不定在那个方法就会用到， 说不定在那段代码就会用到。所以不会轻易的回收，作为root可以，  警告： 不要定义太多的静态变量，影响垃圾回收。</p>
<h4 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h4><p>在jdk1.2后， 引用分为强引用，弱引用，软引用， 虚引用。 区分这么多都是为了方便垃圾回收</p>
<p>强引用没什么好说的</p>
<p><strong>软引用</strong>， 到内存不够的时候回进行回收， java.lang.ref.SoftReference  这个特性很适合作为缓存来使用</p>
<p><strong>弱引用</strong>， 在垃圾回收线程扫描到之前都会存活的内存，垃圾扫描到就会回收。  垃圾线程是一个优先级很低的线程， 所以弱引用还是有一定的存活时间的 WeakReference</p>
<p>？？？ 当一个对象呗垃圾回收后， 后面程序再次使用到这个对象咋办，  是根据class定义重新生成这个对象吗， 还是报空指针异常</p>
<h4 id="虚引用"><a href="#虚引用" class="headerlink" title="虚引用"></a>虚引用</h4><p>丝毫不会对垃圾回收产生影响， 唯一作用就是在对象被回收的时候收到一个系统通知</p>
<p><strong>虚引用</strong>顾名思义，就是<strong>形同虚设</strong>。与其他几种引用都不同，<strong>虚引用</strong>并<strong>不会</strong>决定对象的<strong>生命周期</strong>。如果一个对象<strong>仅持有虚引用</strong>，那么它就和<strong>没有任何引用</strong>一样，在任何时候都可能被垃圾回收器回收。</p>
<p><strong>应用场景：</strong></p>
<p><strong>虚引用</strong>主要用来<strong>跟踪对象</strong>被垃圾回收器<strong>回收</strong>的活动。 <strong>虚引用</strong>与<strong>软引用</strong>和<strong>弱引用</strong>的一个区别在于：</p>
<p>虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时，如果发现它还有虚引用，就会在回收对象的内存之前，把这个虚引用加入到与之关联的引用队列中。</p>
<p>​    String str = new String(“abc”);     ReferenceQueue queue = new ReferenceQueue();     // 创建虚引用，要求必须与一个引用队列关联     PhantomReference pr = new PhantomReference(str, queue);</p>
<p>程序可以通过判断引用<strong>队列</strong>中是否已经加入了<strong>虚引用</strong>，来了解被引用的对象是否将要进行<strong>垃圾回收</strong>。如果程序发现某个虚引用已经被加入到引用队列，那么就可以在所引用的对象的<strong>内存被回收之前</strong>采取必要的行动。</p>
<h4 id="对象的生存还是死亡"><a href="#对象的生存还是死亡" class="headerlink" title="对象的生存还是死亡"></a>对象的生存还是死亡</h4><p>当对象可达性是否的时候， 就会判断对象的finalize()是否有必要执行， 当对象的finalize没有被覆盖或者已经被虚拟机调用过， 就会在之后的垃圾回收中回收掉，  如果finalize被覆盖切未被执行，就会把对象引用放入引用队列中， 后面会有单独的低优先级线程遍历引用队列来执行finalize方法。 如果对象的finalize方法中再次设置对象被强关联， 这个对象就逃过了被回收的命运。 但是逃出升天的机会只有一次，because finalize only run once  。 如果finalize中方法执行缓慢或者死循环就会导致垃圾回收系统崩溃。  finalize被覆盖的方法之后被调用一次， 没有覆盖的方法就是空方法， 没有必要调用。 </p>
<h4 id="方法区的垃圾回收"><a href="#方法区的垃圾回收" class="headerlink" title="方法区的垃圾回收"></a>方法区的垃圾回收</h4><p>方法区垃圾回收jvm没有规范定义， 可以不用实现，但是hotspot有垃圾回收。 比如没有引用的字符串常量， 一些类， 字段， 方法的符号引用， </p>
<p>符号引用： 一些类中的静态常量是另一个类的静态常量</p>
<p>public interface Intf {         public static String str = “abcde”;     public static int ival = new Random().nextInt();  } public class T {     public static int tint = Intf.ival;  //这就是一个符号引用 }</p>
<p>一个类要被卸载（回收）条件： </p>
<p>l  类的所有对象都被回收</p>
<p>l  这个类的类加载器被回收</p>
<p>l  类对应的java.lang.Class对象没有任何地方引用，即无法通过反射获得这个类</p>
<p>找到垃圾对象好就要释放内存， 释放的方法有： </p>
<h3 id="标记清除的内存回收方法"><a href="#标记清除的内存回收方法" class="headerlink" title="标记清除的内存回收方法"></a>标记清除的内存回收方法</h3><p>直接就在内存中把垃圾回收算法标记的垃圾内存释放， </p>
<p>优点： 次方法简单无脑，高效</p>
<p>缺点： 会产生许多不连续的内存碎片， 不利于对象的内存分配</p>
<h3 id="对半复制清除方法"><a href="#对半复制清除方法" class="headerlink" title="对半复制清除方法"></a>对半复制清除方法</h3><p>将内存分成两半， 每次只是使用其中的一半，当一半用完了进行垃圾回收的时候， 就是把存活的对象复制到空闲的另一半， 在统一清除释放用完的那一半内存， 到下次内存回收时候，反过来</p>
<p>优点： 简单无脑，高效， 不会产生内存碎片</p>
<p>缺点： 浪费部分空闲内存， 对象的复制需要时间， 需要老年代来担保装下多余的对象。 老年代上面没有人可以担保了（永久代不行）。 所以这个方法只能用于新生代</p>
<p>现在的商业虚拟机的新生代都是使用对半清除算法， 因为现在的硬件发展很快，内存都是够用的， 还有就是新生代中的对象98%都是朝生夕死的对象，  两半区域不用按照1:1的比例划分， 而是按照1:8:1的比例分成三块的， 每次只是使用其中的1和8两块， 内存浪费率是10%. 当剩下的比例1的区域不足以放下存活的对象的时候，就会把对象放入老年代中</p>
<h4 id="标记整理算法"><a href="#标记整理算法" class="headerlink" title="标记整理算法"></a>标记整理算法</h4><p>主要用于老年代对象， 标记回收了内存中的部分对象后， 就让内存中存活对象都往前移动， 把空闲内存都填满， 这样空闲内存自然就回到最后面了。 </p>
<h2 id="hotspot虚拟机的回收算法"><a href="#hotspot虚拟机的回收算法" class="headerlink" title="hotspot虚拟机的回收算法"></a>hotspot虚拟机的回收算法</h2><p>可达性分析， 需要从运行栈和一些常量和静态属性开始遍历， 然而现在的虚拟机中运行栈和一些常量和静态属性都十分庞大，遍历需要耗时很多， 并且进行可达性遍历的时候， 虚拟机需要暂停所有的其他执行线程。 这就很影响性能了。</p>
<p>hotspot会将一些引用统一存放在一个特定数据结构OopMap中， 进行可达性遍历的时候直接遍历这块区域， 能明显提高效率。</p>
<h4 id="安全点"><a href="#安全点" class="headerlink" title="安全点"></a>安全点</h4><p>可达性遍历需要暂停线程，而线程当然不是随便就能暂停的， 必须运行到安全点才能暂停。 由此可以知道多线程的实现是多么麻烦了， 怪不得pythom没有多线程的实现。 而安全点往往是 方法调用， 循环跳转， 异常跳转等节点处。 </p>
<p>暂停的方法有两种，</p>
<p>l  先全部暂停所有线程， 如果有不在安全点的线程， 就让他继续执行到安全点。 这明明是一个碰运气的暂停方法</p>
<p>l  先做一个暂停标记， 每个线程运行到安全点的时候看下标记是否为true, 为真就暂停，</p>
<p>​            hotspot就是使用这个方法暂停的</p>
<h4 id="安全区"><a href="#安全区" class="headerlink" title="安全区"></a>安全区</h4><p>如果线程在阻塞中， 他就没有办法进入安全点了， 所以线程可以在进入阻塞前标记自己进入安全区了，  可达性分析就可以继续进行了， 当线程阻塞完后， 出来之前先看下暂停标记， 如果是true。 整个线程就暂停。 这样就保证了GC回收的安全</p>
<h2 id="hotspot的多种商用垃圾收集器"><a href="#hotspot的多种商用垃圾收集器" class="headerlink" title="hotspot的多种商用垃圾收集器"></a>hotspot的多种商用垃圾收集器</h2><h3 id="Serial收集器，-新生代都可以用"><a href="#Serial收集器，-新生代都可以用" class="headerlink" title="Serial收集器， 新生代都可以用"></a>Serial收集器， 新生代都可以用</h3><p>单线程的垃圾垃圾收集器。复制清除算法，   当工作线程全部停止后， 只有这一个线程来垃圾收集。</p>
<h4 id="ParNew收集器，新生代都可以用"><a href="#ParNew收集器，新生代都可以用" class="headerlink" title="ParNew收集器，新生代都可以用"></a>ParNew收集器，新生代都可以用</h4><p>Serial收集器的多线程版本，复制清除算法，  可以与CMS收集器合作</p>
<h4 id="Parallel-Scavenge收集器"><a href="#Parallel-Scavenge收集器" class="headerlink" title="Parallel Scavenge收集器"></a>Parallel Scavenge收集器</h4><p>并行（多线程）的复制清除垃圾收集器，  一种控制吞吐量的垃圾收集器。 用于需要快速响应的程序中，比如桌面程序</p>
<h3 id="Serial-Old收集器，-老年代"><a href="#Serial-Old收集器，-老年代" class="headerlink" title="Serial Old收集器， 老年代"></a>Serial Old收集器， 老年代</h3><p>单线程老年代收集器。 复制整理算法， </p>
<h4 id="ParNew-Old收集器，-老年代"><a href="#ParNew-Old收集器，-老年代" class="headerlink" title="ParNew Old收集器， 老年代"></a>ParNew Old收集器， 老年代</h4><h4 id="ParNew的老年代版本，"><a href="#ParNew的老年代版本，" class="headerlink" title="ParNew的老年代版本，"></a>ParNew的老年代版本，</h4><h2 id="CMS收集器"><a href="#CMS收集器" class="headerlink" title="CMS收集器"></a>CMS收集器</h2><p>以获得最短停顿时间为目标的收集器， B/S服务器上普遍采用。</p>
<p>就是将标记清除的步骤拆分四部， 其中两步并行，两步串行来提高效率。</p>
<ol>
<li><p>初始标记 : 需要暂停整个jvm， 仅仅只是单线程标记下GC roots直接关联的对象. 速度很快</p>
</li>
<li><p>并发标记: 就是继续第一步的过程, 继续往下可达性遍历. </p>
</li>
<li><p>重新标记: 暂停整个jvm.  多线程修正下并发标记期间因为因为程序运行而导致的标记变动</p>
</li>
<li><p>并发清除: 清除前面几步标记的垃圾对象, 不暂停jvm. 并发清除的</p>
</li>
<li><p>重置线程: 将之前的标记都清空掉, 从头开始</p>
</li>
</ol>
<p>优点: 并发收集, 低停顿. </p>
<p>缺点: </p>
<ol start="6">
<li><p>对CPU资源敏感(因为需要并发处理)</p>
</li>
<li><p>无法处理浮动垃圾, 并发的过程中程序也在随时产生垃圾,  当浮动垃圾过多的时候会导致fullGC</p>
</li>
</ol>
<p>在jdk6中老年代的CMS的启动阈值时92%. </p>
<p>\3. 标记清除的算法. 会产生大量的空间碎片. 会给大对象的的内存分配带来麻烦.  CMS有一个默认开关,用于在CMS顶不住的时候, 会进行一次空间碎片的整理,  </p>
<p>​             </p>
<h2 id="G1收集器"><a href="#G1收集器" class="headerlink" title="G1收集器"></a>G1收集器</h2><p>是一款面向服务端的垃圾收集器.  思想是将整个内存化整为零.  从而实现高效的垃圾回收,  但是化整为零导致这个收集器的实现很复杂.  在JDK7中G1就可以配置后使用, JDK9中G1已经是默认垃圾收集器</p>
<p>特点: </p>
<p>l  并行与并发:  充分利用doCPU 进行不停顿的垃圾收集</p>
<p>l  分代收集: </p>
<p>l  空间整合: CMS从整体来看是基于标记整理算法来实现的,  但是在局部又是使用复制算法实现. 这个特性结合了两种算法. 可以有效的减少内存碎片</p>
<p>l  可预测的停顿: G1除了追求低停顿外. 还建立可以预测的停顿时间模型. 可以让用户设置在指定的时间内, 用于垃圾收集的时间. </p>
<p>G1将整个内存分成很多个独立的区域Region.  虽然还是保留新生代和老年代的概率, 但是新生代和老年代不在是物理隔离的.   他们都是一部分Region的集合(不需要连续). </p>
<p>G1之所以可以指定停顿时间是因为, 他可以在特定的Region区域内进行垃圾收集. 当指定的时间较少的时候,  就在较少的Region内垃圾回收.  G1还实时统计各个Region的垃圾数量. 并排一个优先级</p>
<p>多个Region中的对象可能互相引用, 在扫描可达性的时候, 岂不是要扫描很多Region吗? </p>
<p>G1在每一个Region中都维护一个Remembased Set来避免全堆扫描. 就是在程序运行中记录下来对象两个对象(不同的Region中)的之间的引用关系. 记录保存在被引用对象的Region中.  这个标记可以达到避免全堆扫描的效果</p>
<p>我的看法: 全堆扫描是无法避免的, Remembased Set只是让全堆扫描的效率更高效吧</p>
<h1 id="GC日志"><a href="#GC日志" class="headerlink" title="GC日志"></a>GC日志</h1><p>阅读GCr日志是处理java虚拟机内存问题的基础技能.   每个收集器的日志格式都不相同.  但是设计者尽量保证了不同收集器的日志的一定 一致性. </p>
<p>示例: </p>
<p>33.125: [GC[DefNew:3324K-&gt;152K(3712K), 0.0025925 secs]3324K-&gt;152K( 11904K), 0.0031680 secs] 100.667: [FullGC[Tenured: 0 K-&gt;210K(10240K) , 0.0149142secs]4603K-&gt;210K( 19456K) , [Perm: 2999K-&gt;2999K( 21248K) ], 0.0150007 secs][Times: user=0.01 sys=0.00, real=0.02 secs]</p>
<p>l  33.125，100.667：代表了GC发生的时间，这个数字的含义是从Java虚拟机启动以来经过的秒数</p>
<p>l  [GC 和 [FullGC ： 说明了此次垃圾回收停顿类型。如果有“Full”，说明了本次GC发生了Stop-The-World(STW)。 如果是调用了System.gc()方法触发的收集，这里将显示”[FullGC(System)”</p>
<p>l  [DefNew 、[Tenured、[Perm：表示GC发送的区域，与使用GC收集器密切相关 </p>
<p>n  使用的Serial收集器中的新生代名为“Default New Generation”所以显示的是“[DefNew”</p>
<p>n  使用的ParNew收集器中的新生代名为“Parallel New Generation”所以显示的是“[ParNew”</p>
<p>n  使用的Parallel Scavenge收集器中的新生代名为“Parallel Scavenge Young Generation”所以显示的是“[PSYoungGen”</p>
<p>n  老年代Tenured和永久代Perm同理，具体名称依旧由使用的收集器决定</p>
<p>l  [..3324K-&gt;152K(3712K)…]：格式含义是“GC前该内存区域已使用的容量-&gt;GC后该内存区域已使用容量（该内存区域总容量）”</p>
<p>l  […]3324K-&gt;152K( 11904K)…：格式含义是“GC前Java堆已使用容量-&gt;GC后Java堆已使用容量（Java堆总容量）”</p>
<p>l  0.0031680 secs：表示该内存区域GC所占用的时间，单位秒。 </p>
<p>n  有的收集器还会给出更具体的时间 “[Times: user=0.01 sys=0.00, real=0.02 secs]”</p>
<p>分别代表：用户态消耗的CPU时间、内核态消耗的CPU时间和操作从开始到结束所经过            的墙钟时间（Wall Clock Time）。</p>
<p>l  TIP：墙钟时间（Wall Clock Time）包括了各种非运算的等待耗时，如磁盘IO。而CPU时间不包括这些耗时</p>
<h2 id="内存分配"><a href="#内存分配" class="headerlink" title="内存分配"></a>内存分配</h2><h3 id="1-对象优先在Eden中分配"><a href="#1-对象优先在Eden中分配" class="headerlink" title="1. 对象优先在Eden中分配"></a>1. 对象优先在Eden中分配</h3><p>当Eden中内存不够分配的时候, 就会进行一次Minor GC .   记住Eden的收集算法 1:8:1的复制清除算法</p>
<h3 id="2-大对象直接进入老年代"><a href="#2-大对象直接进入老年代" class="headerlink" title="2. 大对象直接进入老年代"></a>2. 大对象直接进入老年代</h3><p>大对象经常会导致还有还多内存可用的时候就会进行垃圾回收. 程序中应该尽量避免大对象, 更应避免短命大对象. </p>
<h3 id="3-长期存活的对象将会进入老年代"><a href="#3-长期存活的对象将会进入老年代" class="headerlink" title="3. 长期存活的对象将会进入老年代"></a>3. 长期存活的对象将会进入老年代</h3><p>虚拟机会为每个对象定义一个年龄计数器,   一次Minor GC后还存活的对象的年龄加一,  到达默认的15岁的时候就会进入老年代中.  默认15岁的年龄可以自定义.   当内存不够的时候, 年龄也不一定要达到15岁才进入老年代. </p>
<h3 id="4-空间分配担保"><a href="#4-空间分配担保" class="headerlink" title="4. 空间分配担保"></a>4. 空间分配担保</h3><p>当Eden仅需Minor GC的时候, 会检查老年代中的<strong>剩余最大连续内存</strong>是否可以全部装下Eden中的对象, </p>
<p>l  如果可以的装下, Eden就发现进行Minor GC. </p>
<p>l  如果无法装下,  就检查<strong>剩余最大连续内存 是否都大于历次晋升老年代的对象的平均大小.  如果大于就冒险进行一次Minor GC.  如果小于, 就会进行一次FullGC .</strong> </p>
<h1 id="类文件结构"><a href="#类文件结构" class="headerlink" title="类文件结构"></a>类文件结构</h1><p>jvm 至于Class文件关联.  只运行class文件.  而与生成class文件的语言无关,  这就导致了很多的jvm上的语言. Kotlin,  Groovy等,  也产生了一些字节码增强的库, 最有名的是ASM 和 CGLIB </p>
<p>Class类的</p>
<p>l  前四个字节 称为魔数. 用来确定这个文件是否是一个可以被虚拟机接收的class文件, 与图片的格式的魔数一样, class的魔数是 0xCAFFEBABE(咖啡宝贝?) </p>
<p>l  接下来四个字节存储的是class文件的版本号,  5和6字节是次版本号,  7和8字节是主版本号, 高版本的jdk可以兼容低版本的class</p>
<p>l  接下来是常量池入口, 常量池可以理解成java中的资源仓库. 是Class中与其他结构(项目)关联最多的部分, 也是最占用空间的数据结构</p>
<p>l  常量池之后是访问标志: 标志这个文件是接口还是类, 是public还是private等 是final 还是static, </p>
<p>l  接下来是类索引和父类索引. 记录这个类的继承和实现接口的关系</p>
<p>l  字段表集合: 记录类中有那些字段</p>
<p>l  方法表集: 记录类中的方法</p>
<p>l  属性表集合: 在Class文件, 字段表, 方法表都可以携带自己的属性集合, 用于描述某些场景专有的信息,  属性表的限制不太严格, 任何人都可以写入属性, 虚拟机会忽略掉不认识的属性. </p>

      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/深入理解java虚拟机/" rel="tag"># 深入理解java虚拟机</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/03/22/java/java的内存屏障/" rel="next" title="内存屏障（Memory barrier）">
                <i class="fa fa-chevron-left"></i> 内存屏障（Memory barrier）
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/04/03/虚拟机类加载机制/" rel="prev" title="虚拟机类加载机制">
                虚拟机类加载机制 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name">xiezc</p>
              <p class="site-description motion-element" itemprop="description">主要一些技术文章, 有些文章是转载的</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">72</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                
                  <span class="site-state-item-count">1</span>
                  <span class="site-state-item-name">分类</span>
                
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                
                  <span class="site-state-item-count">20</span>
                  <span class="site-state-item-name">标签</span>
                
              </div>
            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#1，-程序计数器，每个线程私有"><span class="nav-number">1.</span> <span class="nav-text">1， 程序计数器，每个线程私有</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-虚拟机栈-每个线程独有"><span class="nav-number">2.</span> <span class="nav-text">2. 虚拟机栈   每个线程独有</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4，java的堆，-所有线程共享的内存区域，"><span class="nav-number">3.</span> <span class="nav-text">4，java的堆，  所有线程共享的内存区域，</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#堆还可以细分成老年代和新生代区域"><span class="nav-number">3.1.</span> <span class="nav-text">堆还可以细分成老年代和新生代区域</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-方法区，-各个线程共享的区域"><span class="nav-number">4.</span> <span class="nav-text">5. 方法区， 各个线程共享的区域</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#元空间的特性"><span class="nav-number">4.0.1.</span> <span class="nav-text">元空间的特性</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#如果GC发现某个类加载器不再存活了，会把相关的空间整个回收掉元空间的内存分配模型"><span class="nav-number">4.0.2.</span> <span class="nav-text">如果GC发现某个类加载器不再存活了，会把相关的空间整个回收掉元空间的内存分配模型</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#运行时常量池，-也是在方法区中"><span class="nav-number">4.1.</span> <span class="nav-text">运行时常量池， 也是在方法区中</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#直接内存"><span class="nav-number">4.2.</span> <span class="nav-text">直接内存</span></a></li></ol><li class="nav-item nav-level-1"><a class="nav-link" href="#垃圾回收机制和内存分配"><span class="nav-number"></span> <span class="nav-text">垃圾回收机制和内存分配</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#引用计数算法"><span class="nav-number">0.1.</span> <span class="nav-text">引用计数算法</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#可达性分析算法，-主流的jvm都是使用这个算法"><span class="nav-number">0.1.1.</span> <span class="nav-text">可达性分析算法， 主流的jvm都是使用这个算法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#引用"><span class="nav-number">0.1.2.</span> <span class="nav-text">引用</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#虚引用"><span class="nav-number">0.1.3.</span> <span class="nav-text">虚引用</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#对象的生存还是死亡"><span class="nav-number">0.1.4.</span> <span class="nav-text">对象的生存还是死亡</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#方法区的垃圾回收"><span class="nav-number">0.1.5.</span> <span class="nav-text">方法区的垃圾回收</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#标记清除的内存回收方法"><span class="nav-number">0.2.</span> <span class="nav-text">标记清除的内存回收方法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#对半复制清除方法"><span class="nav-number">0.3.</span> <span class="nav-text">对半复制清除方法</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#标记整理算法"><span class="nav-number">0.3.1.</span> <span class="nav-text">标记整理算法</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#hotspot虚拟机的回收算法"><span class="nav-number">1.</span> <span class="nav-text">hotspot虚拟机的回收算法</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#安全点"><span class="nav-number">1.0.1.</span> <span class="nav-text">安全点</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#安全区"><span class="nav-number">1.0.2.</span> <span class="nav-text">安全区</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#hotspot的多种商用垃圾收集器"><span class="nav-number">2.</span> <span class="nav-text">hotspot的多种商用垃圾收集器</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Serial收集器，-新生代都可以用"><span class="nav-number">2.1.</span> <span class="nav-text">Serial收集器， 新生代都可以用</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#ParNew收集器，新生代都可以用"><span class="nav-number">2.1.1.</span> <span class="nav-text">ParNew收集器，新生代都可以用</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#Parallel-Scavenge收集器"><span class="nav-number">2.1.2.</span> <span class="nav-text">Parallel Scavenge收集器</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Serial-Old收集器，-老年代"><span class="nav-number">2.2.</span> <span class="nav-text">Serial Old收集器， 老年代</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#ParNew-Old收集器，-老年代"><span class="nav-number">2.2.1.</span> <span class="nav-text">ParNew Old收集器， 老年代</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#ParNew的老年代版本，"><span class="nav-number">2.2.2.</span> <span class="nav-text">ParNew的老年代版本，</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#CMS收集器"><span class="nav-number">3.</span> <span class="nav-text">CMS收集器</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#G1收集器"><span class="nav-number">4.</span> <span class="nav-text">G1收集器</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#GC日志"><span class="nav-number"></span> <span class="nav-text">GC日志</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#内存分配"><span class="nav-number">1.</span> <span class="nav-text">内存分配</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-对象优先在Eden中分配"><span class="nav-number">1.1.</span> <span class="nav-text">1. 对象优先在Eden中分配</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-大对象直接进入老年代"><span class="nav-number">1.2.</span> <span class="nav-text">2. 大对象直接进入老年代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-长期存活的对象将会进入老年代"><span class="nav-number">1.3.</span> <span class="nav-text">3. 长期存活的对象将会进入老年代</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-空间分配担保"><span class="nav-number">1.4.</span> <span class="nav-text">4. 空间分配担保</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#类文件结构"><span class="nav-number"></span> <span class="nav-text">类文件结构</span></a></li></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">xiezc</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  
  <script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
  <script>AV.initialize("eRAHvO0xVYwckgdXIVpfugRG-gzGzoHsz", "57lKp708hde92YeG1xX9MQj1");</script>
  <script>
    function showTime(Counter) {
      var query = new AV.Query(Counter);
      var entries = [];
      var $visitors = $(".leancloud_visitors");

      $visitors.each(function () {
        entries.push( $(this).attr("id").trim() );
      });

      query.containedIn('url', entries);
      query.find()
        .done(function (results) {
          var COUNT_CONTAINER_REF = '.leancloud-visitors-count';

          if (results.length === 0) {
            $visitors.find(COUNT_CONTAINER_REF).text(0);
            return;
          }

          for (var i = 0; i < results.length; i++) {
            var item = results[i];
            var url = item.get('url');
            var time = item.get('time');
            var element = document.getElementById(url);

            $(element).find(COUNT_CONTAINER_REF).text(time);
          }
          for(var i = 0; i < entries.length; i++) {
            var url = entries[i];
            var element = document.getElementById(url);
            var countSpan = $(element).find(COUNT_CONTAINER_REF);
            if( countSpan.text() == '') {
              countSpan.text(0);
            }
          }
        })
        .fail(function (object, error) {
          console.log("Error: " + error.code + " " + error.message);
        });
    }

    function addCount(Counter) {
      var $visitors = $(".leancloud_visitors");
      var url = $visitors.attr('id').trim();
      var title = $visitors.attr('data-flag-title').trim();
      var query = new AV.Query(Counter);

      query.equalTo("url", url);
      query.find({
        success: function(results) {
          if (results.length > 0) {
            var counter = results[0];
            counter.fetchWhenSave(true);
            counter.increment("time");
            counter.save(null, {
              success: function(counter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(counter.get('time'));
              },
              error: function(counter, error) {
                console.log('Failed to save Visitor num, with error message: ' + error.message);
              }
            });
          } else {
            var newcounter = new Counter();
            /* Set ACL */
            var acl = new AV.ACL();
            acl.setPublicReadAccess(true);
            acl.setPublicWriteAccess(true);
            newcounter.setACL(acl);
            /* End Set ACL */
            newcounter.set("title", title);
            newcounter.set("url", url);
            newcounter.set("time", 1);
            newcounter.save(null, {
              success: function(newcounter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(newcounter.get('time'));
              },
              error: function(newcounter, error) {
                console.log('Failed to create');
              }
            });
          }
        },
        error: function(error) {
          console.log('Error:' + error.code + " " + error.message);
        }
      });
    }

    $(function() {
      var Counter = AV.Object.extend("Counter");
      if ($('.leancloud_visitors').length == 1) {
        addCount(Counter);
      } else if ($('.post-title-link').length > 1) {
        showTime(Counter);
      }
    });
  </script>



  

  
<script>
(function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
    }
    else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>


  
  

  

  

  

</body>
</html>
